<!DOCTYPE html><html class="theme-next pisces use-motion" lang="zh-Hans"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta name="theme-color" content="#222"><meta name="baidu-site-verification" content="YdMoGbLGAx"><script src="/lib/pace/pace.min.js?v=1.0.2"></script><link href="/lib/pace/pace-theme-minimal.min.css?v=1.0.2" rel="stylesheet"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="google-site-verification" content="L2XxDS_YgUmo9ng6ga0rnR1w_IcqBAut-k_te_C1TCQ"><link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css"><link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css"><link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css"><link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.ico?v=5.1.4"><link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.ico?v=5.1.4"><meta name="keywords" content="Java, Spring, Spring boot, Machine learning, Deep learning"><meta name="description" content="一个关于@Transactional注解功能的实现，首先是拦截到标有该注解的所属类，为该类创建一个代理类，然后在对应代理方法中开启事务，过程完成后，提交事务、出现异常回滚事务，最终都要关闭事务。SpringAOP进行动态创建代理，需要创建一个切面Aspect，Spring中用Advisor实现。一个Advisor需要包括通知Advice(TransactionInterceptor)和切入点Poi"><meta property="og:type" content="article"><meta property="og:title" content="关于@Transactional源码解读"><meta property="og:url" content="http://gtwlover.gitee.io/2017/10/12/Transactional解读/index.html"><meta property="og:site_name" content="Better Man"><meta property="og:description" content="一个关于@Transactional注解功能的实现，首先是拦截到标有该注解的所属类，为该类创建一个代理类，然后在对应代理方法中开启事务，过程完成后，提交事务、出现异常回滚事务，最终都要关闭事务。SpringAOP进行动态创建代理，需要创建一个切面Aspect，Spring中用Advisor实现。一个Advisor需要包括通知Advice(TransactionInterceptor)和切入点Poi"><meta property="og:updated_time" content="2018-02-12T12:22:24.000Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="关于@Transactional源码解读"><meta name="twitter:description" content="一个关于@Transactional注解功能的实现，首先是拦截到标有该注解的所属类，为该类创建一个代理类，然后在对应代理方法中开启事务，过程完成后，提交事务、出现异常回滚事务，最终都要关闭事务。SpringAOP进行动态创建代理，需要创建一个切面Aspect，Spring中用Advisor实现。一个Advisor需要包括通知Advice(TransactionInterceptor)和切入点Poi"><script type="text/javascript" id="hexo.configurations">var NexT=window.NexT||{},CONFIG={root:"/",scheme:"Pisces",version:"5.1.4",sidebar:{position:"left",display:"post",offset:12,b2t:!1,scrollpercent:!0,onmobile:!1},fancybox:!0,tabs:!0,motion:{enable:!0,async:!1,transition:{post_block:"fadeIn",post_header:"slideDownIn",post_body:"slideDownIn",coll_header:"slideLeftIn",sidebar:"slideUpIn"}},duoshuo:{userId:"gtwlover",author:"博主"},algolia:{applicationID:"GOSX1252HP",apiKey:"eefb124f52fc415c356384bb0ed50bda",indexName:"myblog",hits:{per_page:10},labels:{input_placeholder:"请输入要查找的关键字",hits_empty:"未找到 ${query} 相关结果",hits_stats:"${hits} 条相关条目，使用了 ${time} 毫秒"}}}</script><link rel="canonical" href="http://gtwlover.gitee.io/2017/10/12/Transactional解读/"><title>关于@Transactional源码解读 | Better Man</title><script>!function(e,a,t,n,g,c,o){e.GoogleAnalyticsObject=g,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,c=a.createElement(t),o=a.getElementsByTagName(t)[0],c.async=1,c.src="https://www.google-analytics.com/analytics.js",o.parentNode.insertBefore(c,o)}(window,document,"script",0,"ga"),ga("create","UA-97111795-1","auto"),ga("send","pageview")</script></head><body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans"><div class="container sidebar-position-left page-post-detail"><div class="headband"></div> <a href="https://github.com/gaotingwang"><img style="position:absolute;top:0;right:0;border:0" src="https://camo.githubusercontent.com/652c5b9acfaddf3a9c326fa6bde407b87f7be0f4/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6f72616e67655f6666373630302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png"></a><header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="header-inner"><div class="site-brand-wrapper"><div class="site-meta"><div class="custom-logo-site-title"><a href="/" class="brand" rel="start"><span class="logo-line-before"><i></i></span> <span class="site-title">Better Man</span><span class="logo-line-after"><i></i></span></a></div><p class="site-subtitle">时间以北慕城南，故人以北爱荒凉</p></div><div class="site-nav-toggle"> <button><span class="btn-bar"></span><span class="btn-bar"></span><span class="btn-bar"></span></button></div></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i><br> 首页</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i><br> 归档</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><i class="menu-item-icon fa fa-search fa-fw"></i><br> 搜索</a></li></ul><div class="site-search"><div class="algolia-popup popup search-popup"><div class="algolia-search"><div class="algolia-search-input-icon"><i class="fa fa-search"></i></div><div class="algolia-search-input" id="algolia-search-input"></div></div><div class="algolia-results"><div id="algolia-stats"></div><div id="algolia-hits"></div><div id="algolia-pagination" class="algolia-pagination"></div></div><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span></div></div></nav></div></header><main id="main" class="main"><div class="main-inner"><div class="content-wrap"><div id="content" class="content"><div id="posts" class="posts-expand"><article class="post post-type-normal" itemscope itemtype="http://schema.org/Article"><div class="post-block"><link itemprop="mainEntityOfPage" href="http://gtwlover.gitee.io/2017/10/12/Transactional解读/"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="name" content="脱毛大白兔"><meta itemprop="description" content=""><meta itemprop="image" content="http://gtw.oss-cn-shanghai.aliyuncs.com/myself/my3.jpeg?x-oss-process=style/zoom"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="Better Man"></span><header class="post-header"><h1 class="post-title" itemprop="name headline">关于@Transactional源码解读</h1><div class="post-meta"><span class="post-time"><span class="post-meta-item-icon"><i class="fa fa-calendar-o"></i></span> <span class="post-meta-item-text">发表于</span> <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-10-12T21:13:24+08:00">2017-10-12</time></span> <span class="post-category"><span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-folder-o"></i></span> <span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/Spring/" itemprop="url" rel="index"><span itemprop="name">Spring</span></a></span></span> <span class="post-comments-count"><span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-comment-o"></i></span><a href="/2017/10/12/Transactional解读/#comments" itemprop="discussionUrl"><span class="post-comments-count gitment-comments-count" data-xid="/2017/10/12/Transactional解读/" itemprop="commentsCount"></span></a></span> <span id="/2017/10/12/Transactional解读/" class="leancloud_visitors" data-flag-title="关于@Transactional源码解读"><span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-eye"></i></span> <span class="post-meta-item-text">阅读次数&#58;</span><span class="leancloud-visitors-count"></span></span><div class="post-description"> 一个关于@Transactional注解功能的实现，首先是拦截到标有该注解的所属类，为该类创建一个代理类，然后在对应代理方法中开启事务，过程完成后，提交事务、出现异常回滚事务，最终都要关闭事务。SpringAOP进行动态创建代理，需要创建一个切面Aspect，Spring中用Advisor实现。一个Advisor需要包括通知Advice(TransactionInterceptor)和切入点Pointcut(TransactionAttributeSourcePointcut)。这样Spring在启动时，就会发现定义的Advisor，然后为对应类创建代理类，在具体请求到来时就会调用代理类来实现功能。</div></div></header><div class="post-body" itemprop="articleBody"><p>Spring提供了两种方式来生成代理对象: <code>JDKProxy</code>和<code>Cglib</code>，具体使用哪种方式生成由<code>AopProxyFactory</code>根据<code>AdvisedSupport</code>对象的配置来决定。<strong>默认的策略是如果目标类是接口，则使用JDK动态代理技术，否则使用Cglib来生成代理。</strong></p><p>项目启动时，需要先找出<code>Advisor</code>：通过<code>BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans():List&lt;Advisor&gt;</code>找出注册的<code>Advisor</code>。然后<code>AbstractAutoProxyCreator.wrapIfNecessary()</code>给<code>targetClass</code>生成代理对象。</p><h2 id="Transactional的Advisor注册"><a href="#Transactional的Advisor注册" class="headerlink" title="Transactional的Advisor注册"></a>Transactional的Advisor注册</h2><p>要想<code>Transaction</code>的<code>Advisor</code>被找出来，首先要注册，在关于<code>ProxyTransaction</code>的配置中注册<code>Advisor</code>:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Configuration</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProxyTransactionManagementConfiguration</span> <span class="keyword">extends</span> <span class="title">AbstractTransactionManagementConfiguration</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 这里是返回关于Transaction的Advisor</div><div class="line">     * advisor中set了pointcut、advice</div><div class="line">     * 所以为什么一般说 Advisor = Pointcut + Advice</div><div class="line">     */</div><div class="line">    <span class="meta">@Bean</span>(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)</div><div class="line">    <span class="meta">@Role</span>(BeanDefinition.ROLE_INFRASTRUCTURE)</div><div class="line">    <span class="function"><span class="keyword">public</span> BeanFactoryTransactionAttributeSourceAdvisor <span class="title">transactionAdvisor</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="comment">// 在创建Advisor的时候已经为advisor设置了Pointcut</span></div><div class="line">        BeanFactoryTransactionAttributeSourceAdvisor advisor = <span class="keyword">new</span> BeanFactoryTransactionAttributeSourceAdvisor();</div><div class="line">        advisor.setTransactionAttributeSource(transactionAttributeSource());</div><div class="line">        <span class="comment">// 设置Advice, TransactionInterceptor 实现了Advice</span></div><div class="line">        advisor.setAdvice(transactionInterceptor());</div><div class="line">        advisor.setOrder(<span class="keyword">this</span>.enableTx.&lt;Integer&gt;getNumber(<span class="string">"order"</span>));</div><div class="line">        <span class="keyword">return</span> advisor;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    ...</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>创建<code>BeanFactoryTransactionAttributeSourceAdvisor</code>的时候设定了<code>Pointcut</code>即<code>TransactionAttributeSourcePointcut</code>:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BeanFactoryTransactionAttributeSourceAdvisor</span> <span class="keyword">extends</span> <span class="title">AbstractBeanFactoryPointcutAdvisor</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> TransactionAttributeSource transactionAttributeSource;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 对象创建时也实例化了Pointcut</div><div class="line">     */</div><div class="line">    <span class="keyword">private</span> <span class="keyword">final</span> TransactionAttributeSourcePointcut pointcut = <span class="keyword">new</span> TransactionAttributeSourcePointcut() &#123;</div><div class="line">        <span class="meta">@Override</span></div><div class="line">        <span class="function"><span class="keyword">protected</span> TransactionAttributeSource <span class="title">getTransactionAttributeSource</span><span class="params">()</span> </span>&#123;</div><div class="line">            <span class="keyword">return</span> transactionAttributeSource;</div><div class="line">        &#125;</div><div class="line">    &#125;;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * Set the transaction attribute source which is used to find transaction</div><div class="line">     * attributes. This should usually be identical to the source reference</div><div class="line">     * set on the transaction interceptor itself.</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setTransactionAttributeSource</span><span class="params">(TransactionAttributeSource transactionAttributeSource)</span> </span>&#123;</div><div class="line">        <span class="keyword">this</span>.transactionAttributeSource = transactionAttributeSource;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> Pointcut <span class="title">getPointcut</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.pointcut;</div><div class="line">    &#125;</div><div class="line">  </div><div class="line">  	....</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>在<code>TransactionInterceptor</code>中实现了关于事务的代理功能</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TransactionInterceptor</span> <span class="keyword">extends</span> <span class="title">TransactionAspectSupport</span> <span class="keyword">implements</span> <span class="title">MethodInterceptor</span>, <span class="title">Serializable</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 这里实现事务开启、提交、回滚、关闭</div><div class="line">     */</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(<span class="keyword">final</span> MethodInvocation invocation)</span> <span class="keyword">throws</span> Throwable </span>&#123;</div><div class="line">        <span class="comment">// Work out the target class: may be &#123;@code null&#125;.</span></div><div class="line">        <span class="comment">// The TransactionAttributeSource should be passed the target class</span></div><div class="line">        <span class="comment">// as well as the method, which may be from an interface.</span></div><div class="line">        Class&lt;?&gt; targetClass = (invocation.getThis() != <span class="keyword">null</span> ? AopUtils.getTargetClass(invocation.getThis()) : <span class="keyword">null</span>);</div><div class="line"></div><div class="line">        <span class="comment">// Adapt to TransactionAspectSupport's invokeWithinTransaction...</span></div><div class="line">        <span class="keyword">return</span> invokeWithinTransaction(invocation.getMethod(), targetClass, <span class="keyword">new</span> InvocationCallback() &#123;</div><div class="line">            <span class="meta">@Override</span></div><div class="line">            <span class="function"><span class="keyword">public</span> Object <span class="title">proceedWithInvocation</span><span class="params">()</span> <span class="keyword">throws</span> Throwable </span>&#123;</div><div class="line">                <span class="keyword">return</span> invocation.proceed();</div><div class="line">            &#125;</div><div class="line">        &#125;);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    ...</div><div class="line">&#125;</div></pre></td></tr></table></figure><h2 id="生成代理类"><a href="#生成代理类" class="headerlink" title="生成代理类"></a>生成代理类</h2><p>是通过<code>AbstractAutoProxyCreator.wrapIfNecessary()</code>来为目标类生成代理类，主要步骤分为：①找到指定对象的可用<code>Advisors</code>②通过<code>ProxyFactory</code>给指定对象上加<code>Advice</code>,来生成代理类。</p><h3 id="1-给指定BeanClass寻找自己的Advisor"><a href="#1-给指定BeanClass寻找自己的Advisor" class="headerlink" title="1.给指定BeanClass寻找自己的Advisor"></a>1.给指定BeanClass寻找自己的Advisor</h3><p>找到目标类的可用<code>Advisors</code>,主要思想是通过每个<code>Advisor</code>获取到<code>Pointcut</code>，然后根据<code>Pointcut</code>中<code>MethodMath.maches()</code>进行判断是否符合条件，具体分析下过程：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractAdvisorAutoProxyCreator</span> <span class="keyword">extends</span> <span class="title">AbstractAutoProxyCreator</span> </span>&#123;</div><div class="line">    <span class="comment">/**</span></div><div class="line">     * ｂ（￣▽￣）ｄ这里返回的就是指定对象的Advisor，在生成代理类时会调用这里的方法，先画个表情符</div><div class="line">     */</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="keyword">protected</span> Object[] getAdvicesAndAdvisorsForBean(Class&lt;?&gt; beanClass, String beanName, TargetSource targetSource) &#123;</div><div class="line">        <span class="comment">// 这里会从所有的Advisor中找到指定beanClass上可用的Advisor</span></div><div class="line">        List&lt;Advisor&gt; advisors = findEligibleAdvisors(beanClass, beanName);</div><div class="line">        <span class="keyword">if</span> (advisors.isEmpty()) &#123;</div><div class="line">            <span class="keyword">return</span> DO_NOT_PROXY;</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> advisors.toArray();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">protected</span> List&lt;Advisor&gt; <span class="title">findEligibleAdvisors</span><span class="params">(Class&lt;?&gt; beanClass, String beanName)</span> </span>&#123;</div><div class="line">        <span class="comment">// 找到所有的Advisors</span></div><div class="line">        List&lt;Advisor&gt; candidateAdvisors = findCandidateAdvisors();</div><div class="line">        <span class="comment">// 过滤出符合指定目标的Advisors</span></div><div class="line">        List&lt;Advisor&gt; eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);</div><div class="line">        extendAdvisors(eligibleAdvisors);</div><div class="line">        <span class="keyword">if</span> (!eligibleAdvisors.isEmpty()) &#123;</div><div class="line">            eligibleAdvisors = sortAdvisors(eligibleAdvisors);</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> eligibleAdvisors;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 找符合目标的Advisors</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">protected</span> List&lt;Advisor&gt; <span class="title">findAdvisorsThatCanApply</span><span class="params">(</span></span></div><div class="line">            List&lt;Advisor&gt; candidateAdvisors, Class&lt;?&gt; beanClass, String beanName) &#123;</div><div class="line"></div><div class="line">        ProxyCreationContext.setCurrentProxiedBeanName(beanName);</div><div class="line">        <span class="keyword">try</span> &#123;</div><div class="line">          	<span class="comment">// 怎么从众多Advisors中找出自己可以用的Advisors,实际在这里实现</span></div><div class="line">            <span class="keyword">return</span> AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">finally</span> &#123;</div><div class="line">            ProxyCreationContext.setCurrentProxiedBeanName(<span class="keyword">null</span>);</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>过滤出符合指定目标的<code>Advisors</code>功能在<code>AopUtils.findAdvisorsThatCanApply()</code>中：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AopUtils</span> </span>&#123;</div><div class="line">  </div><div class="line">  	<span class="comment">/**</span></div><div class="line">  	 * 找出可以使用的Advisors</div><div class="line">  	 */</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> List&lt;Advisor&gt; <span class="title">findAdvisorsThatCanApply</span><span class="params">(List&lt;Advisor&gt; candidateAdvisors, Class&lt;?&gt; clazz)</span> </span>&#123;</div><div class="line">        <span class="keyword">if</span> (candidateAdvisors.isEmpty()) &#123;</div><div class="line">            <span class="keyword">return</span> candidateAdvisors;</div><div class="line">        &#125;</div><div class="line">        List&lt;Advisor&gt; eligibleAdvisors = <span class="keyword">new</span> LinkedList&lt;Advisor&gt;();</div><div class="line">        <span class="keyword">for</span> (Advisor candidate : candidateAdvisors) &#123;</div><div class="line">            <span class="comment">// 当前遍历中的Advisor是否可用在指定类中使用，主要靠canApply()方法</span></div><div class="line">            <span class="keyword">if</span> (candidate <span class="keyword">instanceof</span> IntroductionAdvisor &amp;&amp; canApply(candidate, clazz)) &#123;</div><div class="line">                eligibleAdvisors.add(candidate);</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        ....</div><div class="line">        <span class="keyword">return</span> eligibleAdvisors;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * Advisor是否可以使用，就是使用Advisor中Pointcut的match功能进行匹配检测</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">canApply</span><span class="params">(Advisor advisor, Class&lt;?&gt; targetClass, <span class="keyword">boolean</span> hasIntroductions)</span> </span>&#123;</div><div class="line">        <span class="keyword">if</span> (advisor <span class="keyword">instanceof</span> IntroductionAdvisor) &#123;</div><div class="line">            <span class="keyword">return</span> ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (advisor <span class="keyword">instanceof</span> PointcutAdvisor) &#123;</div><div class="line">            PointcutAdvisor pca = (PointcutAdvisor) advisor;</div><div class="line">            <span class="comment">// 这里使用Advisor的Pointcut</span></div><div class="line">            <span class="keyword">return</span> canApply(pca.getPointcut(), targetClass, hasIntroductions);</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">else</span> &#123;</div><div class="line">            <span class="comment">// It doesn't have a pointcut so we assume it applies.</span></div><div class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">canApply</span><span class="params">(Pointcut pc, Class&lt;?&gt; targetClass, <span class="keyword">boolean</span> hasIntroductions)</span> </span>&#123;</div><div class="line">        ...</div><div class="line"></div><div class="line">        <span class="comment">// 这里获取Pointcut的匹配规则，可以类比认识为@Pointcut("execution(* net.begincode.aop_anno.*.*(..))")中的表达式</span></div><div class="line">        MethodMatcher methodMatcher = pc.getMethodMatcher();</div><div class="line"></div><div class="line">        ...</div><div class="line"></div><div class="line">        Set&lt;Class&lt;?&gt;&gt; classes = <span class="keyword">new</span> LinkedHashSet&lt;Class&lt;?&gt;&gt;(ClassUtils.getAllInterfacesForClassAsSet(targetClass));</div><div class="line">		classes.add(targetClass);</div><div class="line">        <span class="keyword">for</span> (Class&lt;?&gt; clazz : classes) &#123;</div><div class="line">            Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);</div><div class="line">          	<span class="comment">// 获取到类中的所有方法，若有一个匹配，就说明Advisor在此中可用使用，直接return true</span></div><div class="line">            <span class="keyword">for</span> (Method method : methods) &#123;</div><div class="line">                <span class="keyword">if</span> ((introductionAwareMethodMatcher != <span class="keyword">null</span> &amp;&amp;</div><div class="line">                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||</div><div class="line">                        <span class="comment">// 这里就相当于对@Pointcut("execution(* net.begincode.aop_anno.*.*(..))")中的表达式是否满足进行判断</span></div><div class="line">                        methodMatcher.matches(method, targetClass)) &#123;</div><div class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p><code>TransactionAttributeSourcePointcut</code>是实现了<code>MethodMatcher</code>的接口，具体的<code>matches()</code>方法是通过调用<code>TransactionAnnotationParser.parseTransactionAnnotation()</code>来判断元素上注解的名字等于<code>Transactional</code>,然后根据<code>元素</code>和<code>Transactional</code>解析出<code>TransactionAttributeSource</code>不为<code>null</code>,则匹配成功。</p><p>具体看看<code>MethodMatcher.matches()</code>是怎么工作的：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * TransactionAttributeSourcePointcut是实现了MethodMatcher的接口</div><div class="line"> */</div><div class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">TransactionAttributeSourcePointcut</span> <span class="keyword">extends</span> <span class="title">StaticMethodMatcherPointcut</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">matches</span><span class="params">(Method method, Class&lt;?&gt; targetClass)</span> </span>&#123;</div><div class="line">        <span class="keyword">if</span> (TransactionalProxy.class.isAssignableFrom(targetClass)) &#123;</div><div class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</div><div class="line">        &#125;</div><div class="line">        TransactionAttributeSource tas = getTransactionAttributeSource();</div><div class="line">        <span class="comment">// 可以看出最后是调用了TransactionAttributeSource的getTransactionAttribute()，是否匹配真正还是要看TransactionAttributeSource对象获取到的TransactionAttribute是否为空</span></div><div class="line">        <span class="keyword">return</span> (tas == <span class="keyword">null</span> || tas.getTransactionAttribute(method, targetClass) != <span class="keyword">null</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>顺藤摸瓜再来看看<code>TransactionAttributeSource</code>到底干了什么：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 可以看出抽象类AbstractFallbackTransactionAttributeSource实现了TransactionAttributeSource，AnnotationTransactionAttributeSource又是继承了AnnotationTransactionAttributeSource</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractFallbackTransactionAttributeSource</span> <span class="keyword">implements</span> <span class="title">TransactionAttributeSource</span> </span>&#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> TransactionAttribute <span class="title">getTransactionAttribute</span><span class="params">(Method method, Class&lt;?&gt; targetClass)</span> </span>&#123;</div><div class="line">        <span class="keyword">if</span> (method.getDeclaringClass() == Object.class) &#123;</div><div class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="comment">// 会先从缓存中寻找</span></div><div class="line">        Object cacheKey = getCacheKey(method, targetClass);</div><div class="line">        Object cached = <span class="keyword">this</span>.attributeCache.get(cacheKey);</div><div class="line">        <span class="keyword">if</span> (cached != <span class="keyword">null</span>) &#123;</div><div class="line">            ...</div><div class="line">            <span class="keyword">return</span> (TransactionAttribute) cached;</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">else</span> &#123;</div><div class="line">            <span class="comment">// 这里是具体得到TransactionAttribute的过程</span></div><div class="line">            TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);</div><div class="line">            <span class="comment">// 找到了放入缓存中</span></div><div class="line">            <span class="keyword">if</span> (txAttr == <span class="keyword">null</span>) &#123;</div><div class="line">                <span class="keyword">this</span>.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);</div><div class="line">            &#125;</div><div class="line">            ...</div><div class="line">            <span class="keyword">return</span> txAttr;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">	</div><div class="line">  	<span class="comment">// 具体实现是通过子类AnnotationTransactionAttributeSource中重写determineTransactionAttribute方法来获取的</span></div><div class="line">    <span class="function"><span class="keyword">protected</span> TransactionAttribute <span class="title">computeTransactionAttribute</span><span class="params">(Method method, Class&lt;?&gt; targetClass)</span> </span>&#123;</div><div class="line">        ...</div><div class="line">        <span class="comment">// First try is the method in the target class.</span></div><div class="line">        TransactionAttribute txAttr = findTransactionAttribute(specificMethod);</div><div class="line">        <span class="keyword">if</span> (txAttr != <span class="keyword">null</span>) &#123;</div><div class="line">            <span class="keyword">return</span> txAttr;</div><div class="line">        &#125;</div><div class="line">        <span class="comment">// Second try is the transaction attribute on the target class.</span></div><div class="line">        txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());</div><div class="line">        <span class="keyword">if</span> (txAttr != <span class="keyword">null</span> &amp;&amp; ClassUtils.isUserLevelMethod(method)) &#123;</div><div class="line">            <span class="keyword">return</span> txAttr;</div><div class="line">        &#125;</div><div class="line">        ...</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AnnotationTransactionAttributeSource</span> <span class="keyword">extends</span> <span class="title">AbstractFallbackTransactionAttributeSource</span></span></div><div class="line">        <span class="keyword">implements</span> <span class="title">Serializable</span> &#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">protected</span> TransactionAttribute <span class="title">findTransactionAttribute</span><span class="params">(Class&lt;?&gt; clazz)</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> determineTransactionAttribute(clazz);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">  	<span class="comment">/**</span></div><div class="line">  	 * 真正解析获取TransactionAttribute对象，是通过AnnotationParser来进行获取的</div><div class="line">  	 */</div><div class="line">    <span class="function"><span class="keyword">protected</span> TransactionAttribute <span class="title">determineTransactionAttribute</span><span class="params">(AnnotatedElement ae)</span> </span>&#123;</div><div class="line">        <span class="keyword">if</span> (ae.getAnnotations().length &gt; <span class="number">0</span>) &#123;</div><div class="line">            <span class="keyword">for</span> (TransactionAnnotationParser annotationParser : <span class="keyword">this</span>.annotationParsers) &#123;</div><div class="line">                <span class="comment">// 这里是重点@Transactional大boss在这里面要出现了</span></div><div class="line">                TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);</div><div class="line">                <span class="keyword">if</span> (attr != <span class="keyword">null</span>) &#123;</div><div class="line">                    <span class="keyword">return</span> attr;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure><p>怀着对<code>@Transactional</code>的好奇来看看<code>AnnotationParser</code>类干了什么，寻找期盼已久的<code>@Transactional</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 默认使用SpringTransactionAnnotationParser</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpringTransactionAnnotationParser</span> <span class="keyword">implements</span> <span class="title">TransactionAnnotationParser</span>, <span class="title">Serializable</span> </span>&#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> TransactionAttribute <span class="title">parseTransactionAnnotation</span><span class="params">(AnnotatedElement ae)</span> </span>&#123;</div><div class="line">        <span class="comment">// 这里这里，就是这里，我们终于找到了Transactional</span></div><div class="line">      	<span class="comment">// 这里用Transactional干了什么呢？先找出元素上的所有注解，然后遍历这些注解集，如果如果注解不是Java包中的注解，判断注解名字等于Transactional后根据元素和注解，解析出AnnotationAttributes</span></div><div class="line">        AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);</div><div class="line">        <span class="keyword">if</span> (attributes != <span class="keyword">null</span>) &#123;</div><div class="line">            <span class="keyword">return</span> parseTransactionAnnotation(attributes);</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">else</span> &#123;</div><div class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><h3 id="2-根据找到的Advisors为指定对象生成代理类"><a href="#2-根据找到的Advisors为指定对象生成代理类" class="headerlink" title="2. 根据找到的Advisors为指定对象生成代理类"></a>2. 根据找到的Advisors为指定对象生成代理类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractAutoProxyCreator</span> <span class="keyword">extends</span> <span class="title">ProxyProcessorSupport</span></span></div><div class="line">        <span class="keyword">implements</span> <span class="title">SmartInstantiationAwareBeanPostProcessor</span>, <span class="title">BeanFactoryAware</span> &#123;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * Create a proxy with the configured interceptors if the bean is</div><div class="line">     * identified as one to proxy by the subclass.</div><div class="line">     * <span class="doctag">@see</span> #getAdvicesAndAdvisorsForBean</div><div class="line">     */</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">postProcessAfterInitialization</span><span class="params">(Object bean, String beanName)</span> <span class="keyword">throws</span> BeansException </span>&#123;</div><div class="line">        <span class="keyword">if</span> (bean != <span class="keyword">null</span>) &#123;</div><div class="line">            Object cacheKey = getCacheKey(bean.getClass(), beanName);</div><div class="line">            <span class="keyword">if</span> (!<span class="keyword">this</span>.earlyProxyReferences.contains(cacheKey)) &#123;</div><div class="line">                <span class="comment">// 给加@Transactional注解的目标对象生成代理类的功能就是在这里实现的</span></div><div class="line">                <span class="keyword">return</span> wrapIfNecessary(bean, beanName, cacheKey);</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> bean;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">wrapIfNecessary</span><span class="params">(Object bean, String beanName, Object cacheKey)</span> </span>&#123;</div><div class="line">        ...</div><div class="line"></div><div class="line">        <span class="comment">// 这里为指定bean找到可用的Advisors，是在第2块有表情符号的注释处开始的，忘了可以翻回去看看</span></div><div class="line">        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, <span class="keyword">null</span>);</div><div class="line">        <span class="keyword">if</span> (specificInterceptors != DO_NOT_PROXY) &#123;</div><div class="line">            <span class="keyword">this</span>.advisedBeans.put(cacheKey, Boolean.TRUE);</div><div class="line">            <span class="comment">// Create proxy if we have advice.</span></div><div class="line">            <span class="comment">// 方法内部通过使用ProxyFactory给目标对象生成了代理类</span></div><div class="line">            Object proxy = createProxy(</div><div class="line">                    bean.getClass(), beanName, specificInterceptors, <span class="keyword">new</span> SingletonTargetSource(bean));</div><div class="line">            <span class="keyword">this</span>.proxyTypes.put(cacheKey, proxy.getClass());</div><div class="line">            <span class="keyword">return</span> proxy;</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="keyword">this</span>.advisedBeans.put(cacheKey, Boolean.FALSE);</div><div class="line">        <span class="keyword">return</span> bean;</div><div class="line">    &#125;</div><div class="line">    ...</div><div class="line">&#125;</div></pre></td></tr></table></figure><h2 id="运行时调用"><a href="#运行时调用" class="headerlink" title="运行时调用"></a>运行时调用</h2><p>请求具体带有<code>@Transactional</code>注解的方法时，这时访问到都是代理对象了（前面说的Spring在启动时，就是在创建代理对象，然后放到IOC容器中），然后调用代理对象的方法。如果是使用CGLIB，就会调用<code>org.springframework.cglib.proxy.MethodInterceptor</code>实现类的<code>intercept()</code>方法，而<code>intercept()</code>内部最终会调用<code>ProxyMethodInvocation.proceed()</code>，然后一路调用到<code>TransactionInterceptor.invoke()</code>(真正的代理功能)。使用<code>JDKDynamicProxy</code>也同理。</p><h2 id="流程梳理"><a href="#流程梳理" class="headerlink" title="流程梳理"></a>流程梳理</h2><p>一个关于<code>@Transactional</code>注解功能的实现，首先是拦截到标有该注解的所属类，为该类创建一个代理类，然后在对应代理方法中<em>开启事务</em>，过程完成后，<em>提交事务</em>、出现异常<em>回滚事务</em>，最终都要<em>关闭事务</em>。</p><p><code>SpringAOP</code>进行动态创建代理，需要创建一个切面<code>Aspect</code>，<code>Spring</code>中用<code>Advisor</code>实现。</p><p>一个<code>Advisor</code>需要包括通知<code>Advice(TransactionInterceptor)</code>和切入点<code>Pointcut(TransactionAttributeSourcePointcut)</code>。</p><p>这样<code>Spring</code>在启动时，就会发现定义的<code>Advisor</code>，然后为对应类创建代理类，在具体请求到来时就会调用代理类来实现功能。</p><h2 id="模拟实现"><a href="#模拟实现" class="headerlink" title="模拟实现"></a>模拟实现</h2><p>首先定义一个像模像样的<code>@Transactional</code>注解:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">@Target(&#123;ElementType.METHOD, ElementType.TYPE&#125;)</div><div class="line">@Retention(RetentionPolicy.RUNTIME)</div><div class="line">@Inherited</div><div class="line">@Documented</div><div class="line">public @interface Transactional &#123;</div><div class="line">    String[] rollbackForClassName() default &#123;&#125;;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>最后定义一个切面<code>Aspect</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 定义切面：Pointcut + Advice</div><div class="line"> */</div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="meta">@Aspect</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TransactionalAspect</span> </span>&#123;</div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 定义一个切点Pointcut，在有Transactional的地方加入切面</div><div class="line">     * 通过<span class="doctag">@within</span>和<span class="doctag">@annotation</span>指定在方法和类级别同时开启</div><div class="line">     */</div><div class="line">    <span class="meta">@Pointcut</span>(<span class="string">"@within(com.gtw.util.transactional.annotation.Transactional) || @annotation(com.gtw.util.transactional.annotation.Transactional)"</span>)</div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">transactionalPoint</span><span class="params">()</span> </span>&#123; &#125;</div><div class="line">	</div><div class="line">  	<span class="comment">///////////////////////以下使用@Around、@After标注的方法就是Advice///////////////////////</span></div><div class="line">  </div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 定制一个环绕通知</div><div class="line">     * <span class="doctag">@param</span> joinPoint 连接点是在应用执行过程中能够插入切面的一给点。</div><div class="line">     * JoinPoint这个点可以是调用方法时，抛出异常时，甚至修改一个字段时。</div><div class="line">     * 切面代码可以利用这些点插入到应用的正常流程中，并添加新的行为。</div><div class="line">     */</div><div class="line">    <span class="meta">@Around</span>(<span class="string">"transactionalPoint()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">around</span><span class="params">(ProceedingJoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable </span>&#123;</div><div class="line">        System.out.println(<span class="string">"-----开启事务-----"</span>);</div><div class="line">        Object obj = joinPoint.proceed();</div><div class="line">        System.out.println(<span class="string">"-----提交事务-----"</span>);</div><div class="line">        <span class="keyword">return</span> obj;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 退出切点之后执行</div><div class="line">     */</div><div class="line">    <span class="meta">@After</span>(<span class="string">"transactionalPoint()"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">close</span><span class="params">()</span> </span>&#123;</div><div class="line">        System.out.println(<span class="string">"-----关闭事务-----"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 执行遇到异常执行</div><div class="line">     */</div><div class="line">    <span class="meta">@AfterThrowing</span>(pointcut = <span class="string">"transactionalPoint()"</span>, throwing = <span class="string">"error"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">rollback</span><span class="params">(JoinPoint joinPoint, Throwable error)</span> <span class="keyword">throws</span> Exception </span>&#123;</div><div class="line">        <span class="keyword">if</span>(error <span class="keyword">instanceof</span> RuntimeException)&#123;</div><div class="line">            System.out.println(<span class="string">"-----运行时异常，回滚事务-----"</span>);</div><div class="line">        &#125;<span class="keyword">else</span> &#123;</div><div class="line">            <span class="comment">// 获取抛出异常的名称</span></div><div class="line">            String exceptionName = error.getClass().getSimpleName();</div><div class="line"></div><div class="line">            <span class="comment">// 优先使用方法级别的注解</span></div><div class="line">            Transactional methodAnnotation = <span class="keyword">this</span>.getMethodTransactional4JoinPoint(joinPoint);</div><div class="line">            <span class="keyword">if</span>(isRollback4ExceptionName(exceptionName, methodAnnotation))&#123;</div><div class="line">                System.out.println(<span class="string">"-----指定异常，回滚事务-----"</span>);</div><div class="line">            &#125;<span class="keyword">else</span> &#123;</div><div class="line">                <span class="comment">// 方法级别没有注解，则获取类级别注解</span></div><div class="line">                Transactional classTransactional = <span class="keyword">this</span>.getClassTransactional4JoinPoint(joinPoint);</div><div class="line">                <span class="keyword">if</span>(isRollback4ExceptionName(exceptionName, classTransactional))&#123;</div><div class="line">                    System.out.println(<span class="string">"-----指定异常，回滚事务-----"</span>);</div><div class="line">                &#125;<span class="keyword">else</span> &#123;</div><div class="line">                    System.out.println(<span class="string">"-----非运行时异常，不回滚事务-----"</span>);</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        System.out.println(<span class="string">"-----关闭事务-----"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 是否为指定异常名称回滚</div><div class="line">     * <span class="doctag">@param</span> exceptionName 异常名称</div><div class="line">     * <span class="doctag">@param</span> transactional transactionalAnnotation</div><div class="line">     * <span class="doctag">@return</span> 是否为指定异常名称回滚</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">isRollback4ExceptionName</span><span class="params">(String exceptionName, Transactional transactional)</span> </span>&#123;</div><div class="line">        <span class="keyword">if</span>(transactional != <span class="keyword">null</span>) &#123;</div><div class="line">            String rollbackNames[] = transactional.rollbackForClassName();</div><div class="line">            <span class="keyword">if</span>(rollbackNames != <span class="keyword">null</span>) &#123;</div><div class="line">                List&lt;String&gt; rollbackFor = Arrays.asList(rollbackNames);</div><div class="line">                <span class="keyword">return</span> rollbackFor.contains(exceptionName);</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 获取连接点对应方法上的Transactional</div><div class="line">     * <span class="doctag">@param</span> joinPoint 连接点</div><div class="line">     * <span class="doctag">@return</span> 连接点对应的方法</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">private</span> Transactional <span class="title">getMethodTransactional4JoinPoint</span><span class="params">(JoinPoint joinPoint)</span> <span class="keyword">throws</span> NoSuchMethodException </span>&#123;</div><div class="line">        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature(); <span class="comment">// 从接口返回方法</span></div><div class="line">        Method method =  methodSignature.getMethod(); <span class="comment">// 如果实现了接口，是返回接口中定义的方法</span></div><div class="line"></div><div class="line">        Transactional methodAnnotation = method.getAnnotation(Transactional.class);</div><div class="line">        <span class="keyword">if</span>(methodAnnotation == <span class="keyword">null</span> &amp;&amp; method.getDeclaringClass().isInterface()) &#123;</div><div class="line">            <span class="comment">// 如果接口中的方法上未加注解，则从实现类中寻找注解</span></div><div class="line">            method = joinPoint.getTarget().getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());</div><div class="line">            methodAnnotation = method.getAnnotation(Transactional.class);</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="keyword">return</span> methodAnnotation;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 获取连接点对应的类</div><div class="line">     * <span class="doctag">@param</span> joinPoint 连接点</div><div class="line">     * <span class="doctag">@return</span> 连接点对应的类</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">private</span> Transactional <span class="title">getClassTransactional4JoinPoint</span><span class="params">(JoinPoint joinPoint)</span> </span>&#123;</div><div class="line">        Class targetClass = joinPoint.getTarget().getClass();</div><div class="line">        <span class="keyword">return</span> (Transactional) targetClass.getAnnotation(Transactional.class);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure><p>然后剩下的生成代理什么的就交给Spring AOP去帮我们做了。</p><p>具体调用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Service</span></div><div class="line"><span class="meta">@Transactional</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">IUserService</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">addUser</span><span class="params">()</span> </span>&#123;</div><div class="line">        System.out.println(<span class="string">"添加用户"</span>);</div><div class="line">        <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="meta">@Transactional</span>(rollbackForClassName = <span class="string">"MyException"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">updateUser</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</div><div class="line">        System.out.println(<span class="string">"修改用户"</span>);</div><div class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> MyException(<span class="string">"检查型异常"</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">deleteUser</span><span class="params">()</span> </span>&#123;</div><div class="line">        System.out.println(<span class="string">"删除用户"</span>);</div><div class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"运行时异常"</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>详细代码，<a href="https://github.com/gaotingwang/skill-util/tree/master/aop-transactional" target="_blank" rel="external">模拟实现Transactional</a></p><p>ps:关于Spring AOP的一些理解，请参考另一篇博客<a href="http://gtwlover.gitee.io/2017/02/27/Spring-AOP-%E4%BD%A0%E4%B8%8D%E5%BE%97%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84%E4%BA%8B/">关于Spring AOP你不得不知道的一些事</a></p></div><div><div><div style="text-align:center;margin-top:35px;color:#ccc;font-size:14px">------------------------------------本文结束<i class="fa fa-paw"></i>感谢阅读------------------------------------</div></div></div><div><div style="padding:10px 0;margin:20px auto;width:90%;text-align:center"><div>坚持技术分享，你的支持将鼓励我继续创作！</div> <button id="rewardButton" disable="enable" onclick='var qr=document.getElementById("QR");"none"===qr.style.display?qr.style.display="block":qr.style.display="none"'> <span>打赏</span></button><div id="QR" style="display:none"><div id="wechat" style="display:inline-block"> <img id="wechat_qr" src="http://gtw.oss-cn-shanghai.aliyuncs.com/reward.jpg" alt="脱毛大白兔 微信支付"><p>微信支付</p></div></div></div></div><footer class="post-footer"><div class="post-nav"><div class="post-nav-next post-nav-item"><a href="/2017/06/21/Sharding/" rel="next" title="数据库分库分表(Sharding)"><i class="fa fa-chevron-left"></i> 数据库分库分表(Sharding)</a></div><span class="post-nav-divider"></span><div class="post-nav-prev post-nav-item"> <a href="/2017/10/14/Transactional/" rel="prev" title="透彻掌握Spring中@Transactional的使用">透彻掌握Spring中@Transactional的使用<i class="fa fa-chevron-right"></i></a></div></div></footer></div></article><div class="post-spread"></div></div></div><div class="comments" id="comments"><div id="gitment-container"></div></div></div><div class="sidebar-toggle"><div class="sidebar-toggle-line-wrap"><span class="sidebar-toggle-line sidebar-toggle-line-first"></span><span class="sidebar-toggle-line sidebar-toggle-line-middle"></span><span class="sidebar-toggle-line sidebar-toggle-line-last"></span></div></div><aside id="sidebar" class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap"> 文章目录</li><li class="sidebar-nav-overview" data-target="site-overview-wrap"> 站点概览</li></ul><section class="site-overview-wrap sidebar-panel"><div class="site-overview"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"> <img class="site-author-image" itemprop="image" src="http://gtw.oss-cn-shanghai.aliyuncs.com/myself/my3.jpeg?x-oss-process=style/zoom" alt="脱毛大白兔"><p class="site-author-name" itemprop="name">脱毛大白兔</p><p class="site-description motion-element" itemprop="description">一路哼哼唧唧，潇潇洒洒，飘飘扬扬地走下去...</p></div><nav class="site-state motion-element"><div class="site-state-item site-state-posts"> <a href="/archives/"><span class="site-state-item-count">51</span> <span class="site-state-item-name">日志</span></a></div><div class="site-state-item site-state-categories"> <a href="/categories/index.html"><span class="site-state-item-count">9</span> <span class="site-state-item-name">分类</span></a></div><div class="site-state-item site-state-tags"> <a href="/tags/index.html"><span class="site-state-item-count">10</span> <span class="site-state-item-name">标签</span></a></div></nav><div id="music163player" style="margin-bottom:-25px"><iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width="2800" height="86" src="//music.163.com/outchain/player?type=2&id=2408566&auto=0&height=66"></iframe></div><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="https://github.com/gaotingwang" target="_blank" title="GitHub"><i class="fa fa-fw fa-github"></i> GitHub</a></span><span class="links-of-author-item"><a href="mailto:code_butcher@sina.com" target="_blank" title="E-Mail"><i class="fa fa-fw fa-envelope"></i> E-Mail</a></span></div><div class="links-of-blogroll motion-element links-of-blogroll-block"><div class="links-of-blogroll-title"><i class="fa fa-fw fa-link"></i> 友情链接</div><ul class="links-of-blogroll-list"><li class="links-of-blogroll-item"> <a href="http://www.ruanyifeng.com/blog/" title="阮一峰日志" target="_blank">阮一峰日志</a></li><li class="links-of-blogroll-item"> <a href="http://blog.didispace.com/" title="程序员DD博客" target="_blank">程序员DD博客</a></li></ul></div></div></section><section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"><div class="post-toc"><div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#Transactional的Advisor注册"><span class="nav-number">1.</span> <span class="nav-text">Transactional的Advisor注册</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#生成代理类"><span class="nav-number">2.</span> <span class="nav-text">生成代理类</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-给指定BeanClass寻找自己的Advisor"><span class="nav-number">2.1.</span> <span class="nav-text">1.给指定BeanClass寻找自己的Advisor</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-根据找到的Advisors为指定对象生成代理类"><span class="nav-number">2.2.</span> <span class="nav-text">2. 根据找到的Advisors为指定对象生成代理类</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#运行时调用"><span class="nav-number">3.</span> <span class="nav-text">运行时调用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#流程梳理"><span class="nav-number">4.</span> <span class="nav-text">流程梳理</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#模拟实现"><span class="nav-number">5.</span> <span class="nav-text">模拟实现</span></a></li></ol></div></div></section></div></aside></div></main><footer id="footer" class="footer"><div class="footer-inner"><div class="copyright">&copy; 2015 &mdash; <span itemprop="copyrightYear">2018</span><span class="with-love"><i class="fa fa-user"></i></span> <span class="author" itemprop="copyrightHolder">脱毛大白兔</span></div></div></footer><div class="back-to-top"><i class="fa fa-arrow-up"></i> <span id="scrollpercent"><span>0</span>%</span></div></div><script type="text/javascript">"[object Function]"!==Object.prototype.toString.call(window.Promise)&&(window.Promise=null)</script><script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script><script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script><script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script><script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script><script type="text/javascript" src="/lib/three/three.min.js"></script><script type="text/javascript" src="/lib/three/three-waves.min.js"></script><script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script><link rel="stylesheet" href="https://aimingoo.github.io/gitmint/style/default.css"><script src="https://aimingoo.github.io/gitmint/dist/gitmint.browser.js"></script><style>a.gitment-editor-footer-tip{display:none}.gitment-container.gitment-footer-container{display:none}</style><script type="text/javascript">function renderGitment(){new Gitmint({id:window.location.pathname,owner:"gaotingwang",repo:"blog-comments",lang:"en",oauth:{client_secret:"9e283c98b95c605df85e3c7ab94459861b670c6d",client_id:"0c1815a490b171e0c853"}}).render("gitment-container")}renderGitment()</script><link rel="stylesheet" href="/lib/algolia-instant-search/instantsearch.min.css"><script src="/lib/algolia-instant-search/instantsearch.min.js"></script><script src="/js/src/algolia-search.js?v=5.1.4"></script><script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js"></script><script>AV.initialize("d1eLP5bbeDvg09d922elyLBr-gzGzoHsz","MCTjBqQOcTRrfvljPi77Fzsm")</script><script>function showTime(e){var t=new AV.Query(e),n=[],o=$(".leancloud_visitors");o.each(function(){n.push($(this).attr("id").trim())}),t.containedIn("url",n),t.find().done(function(e){var t=".leancloud-visitors-count";if(0!==e.length){for(var i=0;i<e.length;i++){var s=e[i],r=s.get("url"),l=s.get("time"),c=document.getElementById(r);$(c).find(t).text(l)}for(i=0;i<n.length;i++){r=n[i],c=document.getElementById(r);var u=$(c).find(t);""==u.text()&&u.text(0)}}else o.find(t).text(0)}).fail(function(e,t){console.log("Error: "+t.code+" "+t.message)})}function addCount(e){var t=$(".leancloud_visitors"),n=t.attr("id").trim(),o=t.attr("data-flag-title").trim(),i=new AV.Query(e);i.equalTo("url",n),i.find({success:function(t){if(t.length>0){var i=t[0];i.fetchWhenSave(!0),i.increment("time"),i.save(null,{success:function(e){$(document.getElementById(n)).find(".leancloud-visitors-count").text(e.get("time"))},error:function(e,t){console.log("Failed to save Visitor num, with error message: "+t.message)}})}else{var s=new e,r=new AV.ACL;r.setPublicReadAccess(!0),r.setPublicWriteAccess(!0),s.setACL(r),s.set("title",o),s.set("url",n),s.set("time",1),s.save(null,{success:function(e){$(document.getElementById(n)).find(".leancloud-visitors-count").text(e.get("time"))},error:function(e,t){console.log("Failed to create")}})}},error:function(e){console.log("Error:"+e.code+" "+e.message)}})}$(function(){var e=AV.Object.extend("Counter");1==$(".leancloud_visitors").length?addCount(e):$(".post-title-link").length>1&&showTime(e)})</script><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script><script type="text/x-mathjax-config">
  MathJax.Hub.Config("MathJax config");
</script><script type="text/x-mathjax-config">
  MathJax.Hub.Queue(function() {
    var all = MathJax.Hub.getAllJax(), i;
    for(i=0; i < all.length; i += 1) {
      all[i].SourceElement().parentNode.className += ' has-jax';
    }
  });
</script><script type="text/javascript" src="custom_mathjax_source"></script></body></html>